﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace CSharpExperiments.Metaprogramming
{
	public static class Meta
	{
		private static BindingFlags flags = BindingFlags.Default
			| BindingFlags.Static
			| BindingFlags.Instance
			| BindingFlags.NonPublic
			| BindingFlags.Public
			| BindingFlags.FlattenHierarchy;

		public static Z Do<Z, T>(String methodName, params Object[] args) where T : class
		{
			return (Z) typeof(T).GetMethod(methodName, args.Select(x => x.GetType()).ToArray()).Invoke(null, args.ToArray());
		}

		public static Z Do<Z>(this Type type, String methodName, params Object[] args)
		{
			return (Z) type.GetMethod(methodName, args.Select(x => x.GetType()).ToArray()).Invoke(null, args.ToArray());
		}

		public static void Do<T>(String methodName, params Object[] args) where T : class
		{
			typeof(T).GetMethod(methodName, args.Select(x => x.GetType()).ToArray()).Invoke(null, args.ToArray());
		}

		public static void Do(this Type type, String methodName, params Object[] args)
		{
			type.GetMethod(methodName, args.Select(x => x.GetType()).ToArray()).Invoke(null, args.ToArray());
		}

		public static T Prop<T>(this Type type, String propName)
		{
			return (T) type.GetProperty(propName, flags).GetValue(null, null);
		}
		
		public static T Prop<T>(this Type type, String propName, T value)
		{
			type.GetProperty(propName, flags).SetValue(null, value, null);
			return value;
		}

		public static bool HasProp(this Type type, String propName)
		{
			return type != null && type.GetProperty(propName, flags) != null;
		}

		public static T Attr<T>(this PropertyInfo prop) where T : Attribute
		{
			return prop.GetCustomAttributes(typeof(T), true).OfType<T>().FirstOrDefault();
		}

		public static List<T> Attrs<T>(this PropertyInfo prop) where T : Attribute
		{
			return prop.GetCustomAttributes(typeof(T), true).OfType<T>().ToList();
		}

		public static bool HasAttr<T>(this PropertyInfo prop) where T : Attribute
		{
			return prop.GetCustomAttributes(typeof(T), true).Any();
		}

		public static T Attr<T>(this Type type) where T : Attribute
		{
			return type.GetCustomAttributes(typeof(T), true).OfType<T>().FirstOrDefault();
		}

		public static List<T> Attrs<T>(this Type type) where T : Attribute
		{
			return type.GetCustomAttributes(typeof(T), true).OfType<T>().ToList();
		}

		public static bool HasAttr<T>(this Type type) where T : Attribute
		{
			return type.GetCustomAttributes(typeof(T), true).Any();
		}

		public static Object New(this Type type)
		{
			return type.GetConstructor(new Type[0]).Invoke(new Object[0]);
		}

		public static Object New(this Type type, params Object[] args)
		{
			return type.GetConstructor(args.Select(x => x.GetType()).ToArray()).Invoke(args);
		}

		public static T New<T>()
		{
			return (T) New(typeof(T));
		}

		public static T New<T>(params Object[] args)
		{
			return (T) New(typeof(T), args);
		}

		public static T New<T>(this Type type)
		{
			return (T) New(type);
		}

		public static T New<T>(this Type type, params Object[] args)
		{
			return (T) New(type, args);
		}
	}
}
